#include <cassert>
#include <vector>

#include <openssl/bn.h>
#include <openssl/ec.h>

#include "openssl_helper.h"

int EcSubsetSumHash(
    const EC_GROUP* group,
    EC_POINT* partial_sum,
    std::vector<EC_POINT*> points,
    const BIGNUM* selector_value) {
  const std::vector<bool> selector_bits = BN2VT(selector_value).ToBoolVector();
  BN_CTX* bn_ctx = BN_CTX_new();
  for (size_t j = 0; j < points.size(); j++) {
    const EC_POINT* point = points[j];
    assert(EC_POINT_cmp(group, partial_sum, point, bn_ctx)==1 && "Adding a point to itself or to its inverse point.");
    if (selector_bits[j]) {
      EC_POINT_add(group, partial_sum, partial_sum, point, bn_ctx);
    }
  }
  for (size_t j = points.size(); j < selector_bits.size(); j++) {
    assert(selector_bits[j] == 0 && "Given selector is too big.");
  }
  BN_CTX_free(bn_ctx);
  return 1;
}

int PedersenHash0(
    const EC_GROUP* group,
    const BIGNUM* x, const BIGNUM* y, 
    const EC_POINT* shift_point,
    std::vector<EC_POINT*> points,
    BIGNUM* res) {
  const size_t n_element_bits = 252;
  BN_CTX* bn_ctx = BN_CTX_new();
  EC_POINT* cur_sum = EC_POINT_new(group);
  EC_POINT_copy(cur_sum, shift_point);

  EcSubsetSumHash(group, cur_sum, std::vector<EC_POINT*>(points.begin(), points.begin() + n_element_bits), x);
  EcSubsetSumHash(group, cur_sum, std::vector<EC_POINT*>(points.begin() + n_element_bits, points.end()), y);
  EC_POINT_get_affine_coordinates(group, cur_sum, res, NULL, NULL);
  // DLOG(INFO) << "calc_hash: " << BN2VT(res);

  EC_POINT_free(cur_sum);
  BN_CTX_free(bn_ctx);
  return 1;
}
